#include <chrono>
#include <gtest/gtest.h>
#include <mutex>
#include <queue>
#include <thread>
#include "MessageHeader.h"

class BufferTestSuite : public ::testing::Test
{
  public:
    static void SetUpTestSuite() {}
    static void TearDownTestSuite() {}
    void        SetUp() override {}
    void        TearDown() override {}

    void SenMessageToSocket(const std::string& msg)
    {
        auto header = xncc::msg::MessageHeader::encode(0, msg.size());
        MockTcpSend(reinterpret_cast<char*>(&header), sizeof(header));
        MockTcpSend(msg.c_str(), msg.size());
    }

    void MockTcpSend(const char* buffer, const size_t len)
    {
        size_t sent = 0;
        while (true) {
            if (sent >= len) {
                break;
            }
            auto res = MockTcpSendOnce(buffer + sent, len - sent);
            sent += res;
        }
    }

    static ssize_t MockTcpRead(int __fd, void* __buf, size_t __nbytes)
    {
        size_t                      hasread = 0;
        std::lock_guard<std::mutex> lk(TcpbufferMutex);
        while (true) {
            if (Tcpbuffer.empty() || hasread == __nbytes) {
                break;
            }
            if (Tcpbuffer.front() == EOF) {
                if (hasread > 0) {
                    return hasread;
                }

                return -1;
            }
            static_cast<char*>(__buf)[hasread] = Tcpbuffer.front();
            Tcpbuffer.pop();
            ++hasread;
        }
        return static_cast<ssize_t>(hasread);
    }

    void MockClose()
    {
        std::lock_guard<std::mutex> lk(TcpbufferMutex);
        Tcpbuffer.push(EOF);
    }
    static std::queue<char> Tcpbuffer;

  private:
    size_t MockTcpSendOnce(const char* buffer, const size_t len)
    {
        if (len == 1) {
            std::this_thread::sleep_for(std::chrono::microseconds(10));
            std::lock_guard<std::mutex> lk(TcpbufferMutex);
            for (size_t i = 0; i < 1; ++i) {
                Tcpbuffer.push(buffer[i]);
            }
            return 1;
        }
        else {
            const size_t mod_val = len >= 1024 ? 1024 : len;
            srand(static_cast<unsigned int>(time(nullptr)));
            const size_t hasend = (static_cast<size_t>(rand()) % mod_val) + 1;
            std::this_thread::sleep_for(std::chrono::microseconds(10));
            std::lock_guard<std::mutex> lk(TcpbufferMutex);
            for (size_t i = 0; i < hasend; ++i) {
                Tcpbuffer.push(buffer[i]);
            }
            return hasend;
        }
    }

    static std::mutex TcpbufferMutex;
};